/**
 * 
 */
package ej.ea;

import java.util.Arrays;
import java.util.Collection;

/**
 * Emulate extensible enums with interfaces.
 * 
 * You cannot write an extensible enum type, you can emulate it by writing an
 * interface to go with a basic enum type that implements the interface
 * 
 * @author sunaina.sharma
 * 
 */
public class InterfaceWithEnums {

	/*
	 * An opcode is an enumerated type whose elements represent operations on
	 * some machine
	 */

	// Emulated extensible enum using an interface
	public interface Operation {
		double apply(double x, double y);
	}

	public enum BasicOperation implements Operation {
		PLUS("+") {
			public double apply(double x, double y) {
				return x + y;
			}
		},
		MINUS("-") {
			public double apply(double x, double y) {
				return x - y;
			}
		},
		TIMES("*") {
			public double apply(double x, double y) {
				return x * y;
			}
		},
		DIVIDE("/") {
			public double apply(double x, double y) {
				return x / y;
			}
		};
		private final String symbol;

		BasicOperation(String symbol) {
			this.symbol = symbol;
		}

		@Override
		public String toString() {
			return symbol;
		}
	}

	// Emulated extension enum
	public enum ExtendedOperation implements Operation {
		EXP("^") {
			public double apply(double x, double y) {
				return Math.pow(x, y);
			}
		},
		REMAINDER("%") {
			public double apply(double x, double y) {
				return x % y;
			}
		};
		private final String symbol;

		ExtendedOperation(String symbol) {
			this.symbol = symbol;
		}

		@Override
		public String toString() {
			return symbol;
		}
	}

	/*
	 * The class literal for the extended operation type (ExtendedOperation.
	 * class) is passed from main to test to describe the set of extended
	 * operations. The class literal serves as a bounded type token.The
	 * admittedly complex declaration for the opSet parameter ((<T extends
	 * Enum<T> & Operation> Class<T>) ensures that the Class object represents
	 * both an enum and a subtype of Operation.
	 */
	private static <T extends Enum<T> & Operation> void test(Class<T> opSet,
			double x, double y) {
		for (Operation op : opSet.getEnumConstants())
			System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
	}

	/*
	 * A second alternative is to use Collection<? extends Operation>, which is
	 * a bounded wildcard type
	 */
	private static void test1(Collection<? extends Operation> opSet, double x,
			double y) {
		for (Operation op : opSet)
			System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
	}

	/*
	 * A minor disadvantage of the use of interfaces to emulate extensible enums
	 * is that implementations cannot be inherited from one enum type to
	 * another. In the case of our Operation example, the logic to store and
	 * retrieve the symbol associated with an operation is duplicated in
	 * BasicOperation and ExtendedOperation
	 */
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		double x = Double.parseDouble("4");
		double y = Double.parseDouble("8");
		test(BasicOperation.class, x, y);
		test1(Arrays.asList(BasicOperation.values()), x, y);
	}
}
